Skip to content

Conversation

ppkarwasz
Copy link
Contributor

Log4j Core 3 has undergone significant modularization and no longer uses optional parser dependencies. This change requires updates to Spring Boot's configuration file detection logic to properly support both Log4j Core 2 and 3.

Summary of Changes

  • Updated configuration file detection
    Spring Boot now detects configuration formats also based on the presence of ConfigurationFactory implementations, instead of only relying on optional parser dependencies (as was the case in Log4j Core 2).

  • Improved classloader usage for reflection
    Reflection logic now uses the classloader that loaded Log4j Core, rather than the one associated with the Spring Boot context, ensuring greater compatibility in modular environments.

  • Adjusted configuration file lookup order
    The lookup now prioritizes configuration files specified via properties over automatically discovered ones, improving consistency with Log4j Core.

  • Support for contextual configuration files
    Files named in the form log4j2<contextName>.<extension> are now also supported.

These changes ensure compatibility with Log4j Core 3 while preserving support for Log4j Core 2, improving Spring Boot's flexibility in detecting and loading user-defined logging configurations.

Note

The configuration file detection logic introduced here could potentially be moved into a future version of Log4j Core itself. For more context, see apache/logging-log4j2#3775.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 12, 2025
Log4j Core 3 has undergone significant modularization and no longer uses optional parser dependencies. This change requires updates to Spring Boot's configuration file detection logic to properly support both Log4j Core 2 and 3.

* **Updated configuration file detection**
  Spring Boot now detects configuration formats based on the presence of `ConfigurationFactory` implementations, instead of relying on optional parser dependencies (as was the case in Log4j Core 2).

* **Improved classloader usage for reflection**
  Reflection logic now uses the classloader that loaded Log4j Core, rather than the one associated with the Spring Boot context, ensuring greater compatibility in modular environments.

* **Adjusted configuration file lookup order**
  The lookup now prioritizes configuration files specified via properties over automatically discovered ones, improving consistency with Log4j Core.

* **Support for contextual configuration files**
  Files named in the form `log4j2<contextName>.<extension>` are now also supported.

These changes ensure compatibility with Log4j Core 3 while preserving support for Log4j Core 2, improving Spring Boot's flexibility in detecting and loading user-defined logging configurations.

> [!NOTE]
> The configuration file detection logic introduced here could potentially be moved into a future version of Log4j Core itself. For more context, see apache/logging-log4j2#3775.

Signed-off-by: Piotr P. Karwasz <[email protected]>
@ppkarwasz ppkarwasz force-pushed the feat/log4j-core-3-configuration branch from 0de4595 to 2c40cad Compare July 12, 2025 17:58
Signed-off-by: Piotr P. Karwasz <[email protected]>
@mhalbritter mhalbritter changed the title Improve Log4j Core Configuration File Detection for Version 3 Improve Log4j Core configuration file detection for Log4j 3 Aug 29, 2025
@mhalbritter mhalbritter self-assigned this Aug 29, 2025
@mhalbritter mhalbritter added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Aug 29, 2025
@mhalbritter mhalbritter added this to the 4.0.x milestone Aug 29, 2025
mhalbritter pushed a commit that referenced this pull request Aug 29, 2025
Log4j Core 3 has undergone significant modularization and no longer uses
optional parser dependencies. This change requires updates to
Spring Boot's configuration file detection logic to properly support
both Log4j Core 2 and 3.

**Updated configuration file detection**
  Spring Boot now detects configuration formats based on the presence of
   ConfigurationFactory implementations, instead of relying on optional
   parser dependencies (as was the case in Log4j Core 2).

**Improved classloader usage for reflection**
  Reflection logic now uses the classloader that loaded Log4j Core,
  rather than the one associated with the Spring Boot context,
  ensuring greater compatibility in modular environments.

* **Adjusted configuration file lookup order**
  The lookup now prioritizes configuration files specified via
  properties over automatically discovered ones, improving consistency
  with Log4j Core.

**Support for contextual configuration files**
  Files named in the form `log4j2<contextName>.<extension>` are now also
  supported.

These changes ensure compatibility with Log4j Core 3 while preserving
support for Log4j Core 2, improving Spring Boot's flexibility in
detecting and loading user-defined logging configurations.

See gh-46409

Signed-off-by: Piotr P. Karwasz <[email protected]>
@mhalbritter mhalbritter modified the milestones: 4.0.x, 4.0.0-M3 Aug 29, 2025
@mhalbritter
Copy link
Contributor

Thanks @ppkarwasz !

@ppkarwasz
Copy link
Contributor Author

@mhalbritter thanks a lot for merging this! 💯

For context: this PR was our contingency plan to ensure Spring Boot 4 is fully compatible with Log4j Core 3. From the SB integration’s perspective, the only real breaking change in 3.x is the modularization of configuration factories.

In the short term (starting with 2.26.0), we’ll provide an additional ConfigurationFactory method:

public Configuration getConfiguration(LoggerContext, String, List<URI>);

as discussed in apache/logging-log4j2#3839. We believe this is the key missing piece that currently forces you to depend on so many Log4j Core internals (UrlConnectionFactory, SslConfigurationFactory, AuthorizationProvider, CompositeConfiguration, and the config-file detection logic).

Since our project is entirely volunteer-driven, we can’t promise to hit the 4.0.0-RC1 deadline, which is why we’re especially grateful that you reviewed and merged this contingency. Thanks again!

@ppkarwasz ppkarwasz deleted the feat/log4j-core-3-configuration branch August 30, 2025 07:11
@mhalbritter
Copy link
Contributor

My pleasure. Please let us know when the new API lands and we can see how and in which version we can use it. Thanks!

@ppkarwasz
Copy link
Contributor Author

@mhalbritter,

We plan to add the new API in version 2.26.0. I will allow you to remove the mergeConfigurations method, but even now you can easily simplify the configuration part of Log4j2LoggingSystem by removing the code that tries to guess which configuration file will be selected by Log4j Core:

private Configuration getConfiguration(@Nullable URI requestedLocation) {
    final LoggerContext ctx = getLoggerContext();
    final ConfigurationFactory factory = ConfigurationFactory.getInstance();

    final URI main = resolveMainConfigLocation(requestedLocation, ctx, factory);
	final List<URI> overrides = getOverrides();

    // For version `2.25.x` or earlier the existing `mergeConfigurations` method can be used
    final List<URI> locations = new ArrayList<>(overrides.size() + 1);
    locations.add(main);
    locations.addAll(overrides);

    return factory.getConfiguration(ctx, null, locations);
}

private URI resolveMainConfigLocation(@Nullable URI requestedLocation,
                                      LoggerContext ctx,
                                      ConfigurationFactory factory) {
    // 1) Explicit override from caller
    if (requestedLocation != null) {
        return requestedLocation;
    }

    // 2) Current configuration (if it has a source URI)
    final Configuration current = ctx.getConfiguration();
    final URI currentUri = current.getConfigurationSource().getURI();
    if (currentUri != null) {
        return currentUri;
    }

    // 3) Named "-spring" configuration (if available)
    final Configuration springCfg = factory.getConfiguration(ctx, "-spring", null);
    if (springCfg != null) {
        final URI springUri = springCfg.getConfigurationSource().getURI();
        if (springUri != null) {
            return springUri;
        }
    }

    // 4) Fallback default
    return URI.create("classpath:org/springframework/boot/logging/log4j2/log4j2.xml");
}

With this approach most of the protected methods from AbstractLoggingSystem are no longer useful:

// Replaced by checking the URI of the current Log4j Core configuration
// No need to know the details of which URI's are checked
protected @Nullable String getSelfInitializationConfig();
protected String[] getStandardConfigLocations();
// Replaced by `getConfiguration(ctx, "-spring", null)`
protected @Nullable String getSpringInitializationConfig();
protected String[] getSpringConfigLocations();
// 
protected void loadConfiguration(LoggingInitializationContext initializationContext, String location, @Nullable LogFile logFile);

Would it be OK to transform these methods into no-ops after the release of 4.x.x or will this break some of you backward compatibility rules?

@mhalbritter
Copy link
Contributor

If that happens after the release of 4.0.0-RC1, this would be classified as an enhancement and would be targeted to 4.1.0. And we also need to do a deprecation cycle for those useless protected methods, as they are public API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants